using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using JGSY.CMS.LowCode.Platform.Application.DTOs;
using JGSY.CMS.LowCode.Platform.Application.Interfaces;
using JGSY.CMS.LowCode.Platform.Domain.Entities;
using JGSY.CMS.LowCode.Platform.Infrastructure.CmsDbContext;
using Microsoft.EntityFrameworkCore;

namespace JGSY.CMS.LowCode.Platform.Application.Services
{
    /// <summary>
    /// 异常日志应用服务实现
    /// 提供异常日志的业务逻辑操作
    /// </summary>
    public class ExceptionLogAppService : IExceptionLogAppService
    {
        private readonly LowCodeDbContext _dbContext;
        private readonly ILogger<ExceptionLogAppService> _logger;

        public ExceptionLogAppService(
            LowCodeDbContext dbContext,
            ILogger<ExceptionLogAppService> logger)
        {
            _dbContext = dbContext;
            _logger = logger;
        }

        public async Task<long> LogExceptionAsync(
            Exception exception,
            string? traceId = null,
            string? correlationId = null,
            string? tenantId = null,
            string? userId = null,
            string? requestPath = null,
            string? httpMethod = null,
            Dictionary<string, object>? additionalData = null)
        {
            try
            {
                var exceptionLog = new ExceptionLog
                {
                    TraceId = traceId ?? Guid.NewGuid().ToString(),
                    CorrelationId = correlationId,
                    ExceptionLevel = DetermineExceptionLevel(exception),
                    ExceptionType = exception.GetType().Name,
                    ExceptionMessage = exception.Message,
                    StackTrace = exception.StackTrace,
                    InnerException = exception.InnerException?.ToString(),
                    Source = exception.Source,
                    TargetSite = exception.TargetSite?.ToString(),
                    HelpLink = exception.HelpLink,
                    Data = exception.Data?.Count > 0 ? System.Text.Json.JsonSerializer.Serialize(exception.Data) : null,
                    UserId = userId,
                    TenantId = tenantId ?? string.Empty,
                    RequestPath = requestPath,
                    HttpMethod = httpMethod,
                    UserAgent = GetUserAgentFromAdditionalData(additionalData) ?? string.Empty,
                    IpAddress = GetIpAddressFromAdditionalData(additionalData),
                    AdditionalData = additionalData != null ? System.Text.Json.JsonSerializer.Serialize(additionalData) : null,
                    Timestamp = DateTime.UtcNow,
                    IsFixed = false
                };

                _dbContext.ExceptionLogs.Add(exceptionLog);
                await _dbContext.SaveChangesAsync();

                return exceptionLog.Id;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "记录异常日志时发生错误");
                return 0;
            }
        }

        public async Task<long> LogCriticalExceptionAsync(Exception exception, ExceptionContext context)
        {
            try
            {
                var exceptionLog = new ExceptionLog
                {
                    TraceId = context.TraceId ?? Guid.NewGuid().ToString(),
                    CorrelationId = context.CorrelationId,
                    ExceptionLevel = "Critical",
                    ExceptionType = exception.GetType().Name,
                    ExceptionMessage = exception.Message,
                    StackTrace = exception.StackTrace,
                    InnerException = exception.InnerException?.ToString(),
                    Source = exception.Source,
                    TargetSite = exception.TargetSite?.ToString(),
                    HelpLink = exception.HelpLink,
                    Data = exception.Data?.Count > 0 ? System.Text.Json.JsonSerializer.Serialize(exception.Data) : null,
                    UserId = context.UserId,
                    UserName = context.UserName,
                    TenantId = context.TenantId ?? string.Empty,
                    RequestPath = context.RequestPath,
                    HttpMethod = context.HttpMethod,
                    UserAgent = context.UserAgent,
                    IpAddress = context.IpAddress,
                    RequestData = context.RequestData != null ? System.Text.Json.JsonSerializer.Serialize(context.RequestData) : null,
                    AdditionalData = context.AdditionalData != null ? System.Text.Json.JsonSerializer.Serialize(context.AdditionalData) : null,
                    Timestamp = DateTime.UtcNow,
                    IsFixed = false
                };

                _dbContext.ExceptionLogs.Add(exceptionLog);
                await _dbContext.SaveChangesAsync();

                return exceptionLog.Id;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "记录严重异常日志时发生错误");
                return 0;
            }
        }

        public async Task<ExceptionLogDto?> GetExceptionLogAsync(long id)
        {
            try
            {
                var exceptionLog = await _dbContext.ExceptionLogs.FindAsync(id);
                if (exceptionLog == null) return null;

                return MapToDto(exceptionLog);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取异常日志时发生错误，ID: {Id}", id);
                return null;
            }
        }

        public async Task<(List<ExceptionLogDto> Items, int TotalCount)> GetExceptionLogsAsync(
            string? tenantId = null,
            string? exceptionLevel = null,
            DateTime? startTime = null,
            DateTime? endTime = null,
            int pageIndex = 1,
            int pageSize = 20)
        {
            try
            {
                var query = _dbContext.ExceptionLogs.AsQueryable();

                if (!string.IsNullOrEmpty(tenantId))
                    query = query.Where(x => x.TenantId == tenantId);

                if (!string.IsNullOrEmpty(exceptionLevel))
                    query = query.Where(x => x.ExceptionLevel == exceptionLevel);

                if (startTime.HasValue)
                    query = query.Where(x => x.Timestamp >= startTime.Value);

                if (endTime.HasValue)
                    query = query.Where(x => x.Timestamp <= endTime.Value);

                var totalCount = await query.CountAsync();

                var items = await query
                    .OrderByDescending(x => x.Timestamp)
                    .Skip((pageIndex - 1) * pageSize)
                    .Take(pageSize)
                    .Select(x => MapToDto(x))
                    .ToListAsync();

                return (items, totalCount);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取异常日志列表时发生错误");
                return (new List<ExceptionLogDto>(), 0);
            }
        }

        public async Task<bool> UpdateFixStatusAsync(long id, bool isFixed, string? fixedBy = null, DateTime? fixedAt = null, string? fixNotes = null)
        {
            try
            {
                var exceptionLog = await _dbContext.ExceptionLogs.FindAsync(id);
                if (exceptionLog == null) return false;

                exceptionLog.IsFixed = isFixed;
                exceptionLog.FixedBy = fixedBy;
                exceptionLog.FixedAt = fixedAt ?? (isFixed ? DateTime.UtcNow : null);
                exceptionLog.FixNotes = fixNotes;

                await _dbContext.SaveChangesAsync();
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "更新异常修复状态时发生错误，ID: {Id}", id);
                return false;
            }
        }

        public async Task<List<ExceptionLogDto>> GetExceptionLogsByCorrelationIdAsync(string correlationId)
        {
            try
            {
                var items = await _dbContext.ExceptionLogs
                    .Where(x => x.CorrelationId == correlationId)
                    .OrderByDescending(x => x.Timestamp)
                    .Select(x => MapToDto(x))
                    .ToListAsync();

                return items;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "根据关联ID获取异常日志时发生错误，CorrelationId: {CorrelationId}", correlationId);
                return new List<ExceptionLogDto>();
            }
        }

        public async Task<ExceptionStatistics> GetExceptionStatisticsAsync(string? tenantId = null, DateTime? startTime = null, DateTime? endTime = null)
        {
            try
            {
                var query = _dbContext.ExceptionLogs.AsQueryable();

                if (!string.IsNullOrEmpty(tenantId))
                    query = query.Where(x => x.TenantId == tenantId);

                if (startTime.HasValue)
                    query = query.Where(x => x.Timestamp >= startTime.Value);

                if (endTime.HasValue)
                    query = query.Where(x => x.Timestamp <= endTime.Value);

                var statistics = new ExceptionStatistics
                {
                    TotalExceptions = await query.CountAsync(),
                    CriticalExceptions = await query.Where(x => x.ExceptionLevel == "Critical").CountAsync(),
                    ErrorExceptions = await query.Where(x => x.ExceptionLevel == "Error").CountAsync(),
                    WarningExceptions = await query.Where(x => x.ExceptionLevel == "Warning").CountAsync(),
                    FixedExceptions = await query.Where(x => x.IsFixed == true).CountAsync(),
                    UnfixedExceptions = await query.Where(x => x.IsFixed == false).CountAsync()
                };

                // 异常类型统计
                statistics.ExceptionTypeCount = await query
                    .GroupBy(x => x.ExceptionType)
                    .Select(g => new { Type = g.Key ?? "Unknown", Count = g.Count() })
                    .ToDictionaryAsync(x => x.Type, x => x.Count);

                // 按小时统计
                if (startTime.HasValue && endTime.HasValue)
                {
                    statistics.ExceptionsByHour = await query
                        .GroupBy(x => x.Timestamp.Hour)
                        .Select(g => new { Hour = g.Key.ToString(), Count = g.Count() })
                        .ToDictionaryAsync(x => x.Hour, x => x.Count);
                }

                return statistics;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "获取异常统计信息时发生错误");
                return new ExceptionStatistics();
            }
        }

        private string DetermineExceptionLevel(Exception exception)
        {
            return exception switch
            {
                OutOfMemoryException or StackOverflowException or AccessViolationException => "Critical",
                InvalidOperationException or InvalidCastException or NotSupportedException => "Error",
                ArgumentException or ArgumentNullException or ArgumentOutOfRangeException => "Warning",
                _ => "Error"
            };
        }

        private string? GetUserAgentFromAdditionalData(Dictionary<string, object>? additionalData)
        {
            return additionalData?.TryGetValue("UserAgent", out var userAgent) == true ? userAgent?.ToString() : null;
        }

        private string? GetIpAddressFromAdditionalData(Dictionary<string, object>? additionalData)
        {
            return additionalData?.TryGetValue("IpAddress", out var ipAddress) == true ? ipAddress?.ToString() : null;
        }

        private ExceptionLogDto MapToDto(ExceptionLog entity)
        {
            return new ExceptionLogDto
            {
                Id = entity.Id,
                TraceId = entity.TraceId,
                CorrelationId = entity.CorrelationId,
                ExceptionLevel = entity.ExceptionLevel,
                ExceptionType = entity.ExceptionType,
                ExceptionMessage = entity.ExceptionMessage,
                StackTrace = entity.StackTrace,
                InnerException = entity.InnerException,
                Source = entity.Source,
                TargetSite = entity.TargetSite,
                HelpLink = entity.HelpLink,
                Data = entity.Data,
                UserId = entity.UserId,
                UserName = entity.UserName,
                TenantId = entity.TenantId,
                RequestPath = entity.RequestPath,
                HttpMethod = entity.HttpMethod,
                UserAgent = entity.UserAgent,
                IpAddress = entity.IpAddress,
                RequestData = entity.RequestData,
                AdditionalData = entity.AdditionalData,
                Timestamp = entity.Timestamp,
                IsFixed = entity.IsFixed,
                FixedBy = entity.FixedBy,
                FixedAt = entity.FixedAt,
                FixNotes = entity.FixNotes,
                CreatedAt = entity.CreatedAt
            };
        }
    }
}
